﻿using Cemit.PolyProto;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Cemit.NetFramework.Logger;
using Debug = Cemit.NetFramework.Logger.Debug;
using Cemit.Awaiter;

namespace Cemit.NetFramework
{
    static partial class PolyNetwork
    {
        [Obsolete("使用帧同步框架进行角色同步")]
        public static event Action<IPlayerData> OnCreatePlayer;
        [Obsolete("使用帧同步框架进行角色同步")]
        public static event Action OnCreatePlayerSucceeded;
        [Obsolete("使用帧同步框架进行角色同步")]
        public static event Action OnCreatePlayerFailed;

        [Obsolete("消息监听相关方法已转移至PolySocket")]
        static Dictionary<Type, MessagesListener> messagesListenersPairs
           = new Dictionary<Type, MessagesListener>();
        [Obsolete("Send时使用callback参数设置回执")]
        static PolySocket.ReceiveCallbacks receiveCallbacks = new PolySocket.ReceiveCallbacks();

        [Obsolete("请使用PolySocket类")]
        static Socket socket;
        [Obsolete("请使用PolySocket类")]
        static Queue<ByteArray> writeQueue;
        [Obsolete("请使用PolySocket类")]
        static ByteArray readBuff = new ByteArray();
        [Obsolete("请使用PolySocket类")]
        static Queue<IMessage> messagesQueue = new Queue<IMessage>();
        [Obsolete("请使用PolySocket类")]
        static int messagesQueueCount = 0;
        [Obsolete("请使用PolySocket类")]
        static float lastPingTime;
        [Obsolete("请使用PolySocket类")]
        static float lastPongTime;
        [Obsolete("请使用PolySocket类")]
        static bool isConnecting = false;
        [Obsolete("请使用PolySocket类")]
        static bool isClosing = false;


        #region 属性
        [Obsolete("使用帧同步框架进行角色同步")]
        public static IPlayerData Player { get; set; }
        #endregion


        /// <summary>
        /// 发送playerData的所有公开成员变量,
        /// 向服务器申请新建一个玩家
        /// (支持的变量类型为Int32,Float,String,Boolean,Vector2,Vector3和Quaternion)
        /// </summary>
        [Obsolete("使用帧同步框架进行角色同步")]
        public static void CreatePlayer(IPlayerData playerData)
        {
            Player = playerData;

            m_MainSocket.Send(new Msg_Lobby_CreatePlayer
            {
                PlayerData = Player.GetMessageData()
            })
            .AddCallback<Msg_Lobby_CreatePlayer>(message => OnMsgCreatePlayer(message, playerData));

            //new Msg_Lobby_CreatePlayer
            //{
            //    PlayerData = Player.GetMessageData()
            //}
            //.Send()
            //.SetCallback<Msg_Lobby_CreatePlayer>(message => OnMsgCreatePlayer(message, playerData));

            //发送信息，向服务器申请新建角色
            //AddMessageListener<MsgCreatePlayer>(message => OnMsgCreatePlayer(message, playerData));
        }

        [Obsolete("使用帧同步框架进行角色同步")]
        private static void OnMsgCreatePlayer(Msg_Lobby_CreatePlayer message, IPlayerData playerData)
        {
            //创建角色失败
            if (message.PlayerData == null)
            {
                Debug.LogError("创建角色失败！");
                MonoHandle.Invoke(OnCreatePlayerFailed);
                return;
            }

            //新建角色成功
            if (message.PlayerData.Id == Player.ID)
            {
                //开始监听服务器获取角色数据的消息

                SetMessageListener<Msg_Lobby_GetPlayerData>(p =>
                {
                    Msg_Lobby_GetPlayerData msgSend = p as Msg_Lobby_GetPlayerData;
                    msgSend.PlayerData = Player.GetMessageData();
                    Send(msgSend);
                });

                //分发事件
                MonoHandle.Invoke(OnCreatePlayerSucceeded);
                MonoHandle.Invoke(OnCreatePlayer, Player.SetValue(message.PlayerData));
                return;
            }

            //别的玩家新建角色的消息
            IPlayerData player = CreatePlayerInstance(message.PlayerData);
            if (player != null)
            {
                MonoHandle.Invoke(OnCreatePlayer, player);
            }

            IPlayerData CreatePlayerInstance(PlayerData data)
            {
                if (!HasDefaultConstructor())
                {
                    Debug.LogError($"创建角色失败：{playerData.GetType().Name}没有默认的无参构造函数！");
                    return null;
                }

                return ((IPlayerData)Activator.CreateInstance(playerData.GetType()))
                    .SetValue(data);

                bool HasDefaultConstructor()
                    => playerData.GetType().GetConstructors().Any(c => c.GetParameters().Length == 0);
            }
        }

        /// <summary>
        /// 处理心跳包
        /// </summary>
        [Obsolete("使用PolySocketPingExtensions的相关方法")]
        private static void ProcessPing()
        {
            if (Time.time - lastPingTime > PingInterval)
            {
                Send(new Msg_Net_Ping());
                lastPingTime = Time.time;
            }

            if (Time.time - lastPongTime > PingInterval * 4)
            {
                Debug.LogError("与服务器的会话超时！");
                Close();
            }
        }

        [Obsolete("直接操作PolySocket")]
        public static void Close()
        {
            if (socket == null || !socket.Connected)
            {
                Debug.LogWarning("关闭服务器失败：服务器已断开或未连接。");
                return;
            }
            if (isConnecting)
            {
                Debug.LogWarning("关闭服务器失败：服务器正在连接中。");
                return;
            }
            if (writeQueue.Count > 0)
            {
                isClosing = true;
            }
            else
            {
                socket.Close();
                MonoHandle.Invoke(OnClosed);
                Debug.LogInfo("服务器已成功关闭。");
            }
        }

        [Obsolete("直接操作PolySocket")]
        public static PolySocket.ReceiveCallbacks Send(this IMessage message)
        {
            if (!IsConnected)
            {
                Debug.LogWarning("消息发送失败：服务器未连接！");
                return receiveCallbacks;
            }
            Debug.Log("发送消息：" + message.GetType().Name);
            byte[] nameBytes = PolyProto.MessageEncoding.EncodeName(message);
            byte[] bodyBytes = PolyProto.MessageEncoding.Encode(message);

            int length = nameBytes.Length + bodyBytes.Length;
            byte[] sendBytes = new byte[2 + length];

            sendBytes[0] = (byte)(length % 256);
            sendBytes[1] = (byte)(length / 256);

            Array.Copy(nameBytes, 0, sendBytes, 2, nameBytes.Length);
            Array.Copy(bodyBytes, 0, sendBytes, 2 + nameBytes.Length, bodyBytes.Length);

            ByteArray byteArray = new ByteArray(sendBytes);
            int count = 0;
            lock (writeQueue)
            {
                writeQueue.Enqueue(byteArray);
                count = writeQueue.Count;
            }
            if (count == 1)
            {
                socket.BeginSend(sendBytes, 0, sendBytes.Length, 0, SendCallback, socket);
            }

            return receiveCallbacks;
        }

        /// <summary>
        /// 为消息设置一个监听，替换掉原本的所有监听
        /// </summary>
        [Obsolete("使用PolySocket.SetMessageListener")]
        public static void SetMessageListener<T>(MessagesListener listener) where T : IMessage
        {
            messagesListenersPairs[typeof(T)] = listener;
        }

        /// <summary>
        /// 为消息添加一个监听
        /// </summary>
        [Obsolete("使用PolySocket.AddMessageListener")]
        public static void AddMessageListener<T>(MessagesListener listener) where T : IMessage
        {
            if (messagesListenersPairs.ContainsKey(typeof(T)))
            {
                messagesListenersPairs[typeof(T)] += listener;
            }
            else
            {
                messagesListenersPairs[typeof(T)] = listener;
            }
        }

        [Obsolete("使用PolySocket.RemoveMessageListener")]
        public static void RemoveMessageListener<T>(MessagesListener listener) where T : IMessage
        {
            if (messagesListenersPairs.ContainsKey(typeof(T)))
            {
                messagesListenersPairs[typeof(T)] -= listener;
                if (messagesListenersPairs[typeof(T)] == null)
                {
                    messagesListenersPairs.Remove(typeof(T));
                }
            }
        }

        [Obsolete("直接使用PolySocket")]
        private static void ProcessMessages()
        {
            if (messagesQueueCount == 0)
            {
                return;
            }

            for (int i = 0; i < MAX_MESSAGE_FIRE; i++)
            {
                IMessage message = null;
                lock (messagesQueue)
                {
                    if (messagesQueue.Count > 0)
                    {
                        message = messagesQueue.Dequeue();
                        messagesQueueCount--;
                    }
                }
                if (message != null)
                {
                    FireMessage(message);
                }
                else
                {
                    break;
                }
            }
        }

        [System.Obsolete("使用PolySocke.Login")]
        public static async Task<LoginResult> Login(string name)
        {
            return await m_MainSocket.Login(name);
        }

        [Obsolete("直接使用PolySocket")]
        private static bool CheckConnected()
        {
            return !(socket == null || !socket.Connected)
                && !isConnecting
                && !isClosing;
        }

        [Obsolete("直接使用PolySocket")]
        private static void FireMessage(IMessage message)
        {
            if (messagesListenersPairs.ContainsKey(message.GetType()))
            {
                messagesListenersPairs[message.GetType()].Invoke(message);
            }

            receiveCallbacks.Invoke(message);
        }

        /// <summary>
        /// 获取大厅列表
        /// </summary>
        [System.Obsolete("使用可等待的方法 AsyncAction<IEnumerable<Data_LobbyInfo>> GetLobbyList()")]
        public static void GetLobbyList(Action<LobbyInfo[]> callback)
        {
            if (!IsConnected)
            {
                Debug.LogWarning("获取大厅列表失败！服务器还未连接！");
                return;
            }

            m_MainSocket
                .Send(new Msg_Main_GetLobbyList())
                .AddCallback<Msg_Main_LobbyInfoList>(
                    (m) => callback?.Invoke(m.ServerInfoList.ToArray()));

        }

        [Obsolete("直接使用PolySocket")]
        private static void SendCallback(IAsyncResult ar)
        {
            if (!(ar.AsyncState is Socket socket) || !socket.Connected)
            {
                Debug.LogError("发送数据失败：服务器已断开！");
                return;
            }

            int count = socket.EndSend(ar); //获取成功发送的字节个数
            Debug.Log("已发送数据：" + count + " bytes");

            bool sendFlag = true;
            ByteArray byteArray;
            lock (writeQueue)
            {
                byteArray = writeQueue.First();
            }

            byteArray.readIndex += count;
            if (byteArray.Length == 0) //全部数据已发送
            {
                lock (writeQueue)
                {
                    writeQueue.Dequeue();
                }
                if (writeQueue.Count > 0)
                {
                    byteArray = writeQueue.First();
                }
                else
                {
                    sendFlag = false;
                }
            }

            if (sendFlag)
            {
                socket.BeginSend(byteArray.bytes, byteArray.readIndex, byteArray.Length, 0, SendCallback, socket);
            }
            else if (isClosing)
            {
                socket.Close();
            }
        }

        [Obsolete("在调用Connect()连接时直接传递PolyServerSettings")]
        private static PolyServerSettings GetSettings()
        {
            return null;

            //            PolyServerSettings serverSettings = PolyServerSettings.LoadSetting();
            //            if (!serverSettings)
            //            {
            //#if UNITY_EDITOR
            //                Debug.LogWarning($"读取{typeof(PolyServerSettings).Name}失败！尝试创建默认设置。");
            //                serverSettings = PolyServerSettings.CreateSettings();
            //                if (!serverSettings)
            //                {
            //                    throw new Exception($"创建{typeof(PolyServerSettings).Name}失败！");
            //                }
            //                Debug.LogInfo("创建默认设置成功。");
            //#else
            //                Debug.LogWarning($"读取{typeof(PolyServerSettings).Name}失败！尝试使用默认设置。");
            //                serverSettings = new PolyServerSettings();
            //#endif
            //            }
            //            Debug.Log($"读取{typeof(PolyServerSettings).Name}成功。");
            //            return serverSettings;
        }

        /// <summary>
        /// 连接回调
        /// </summary>
        [Obsolete("直接操作PolySocket")]
        private static void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                Socket socket = (Socket)ar.AsyncState;
                socket.EndConnect(ar);
                isConnecting = false;
                socket.BeginReceive(
                    readBuff.bytes, readBuff.writeIndex, readBuff.Remain, 0, ReceiveCallback, socket);
                MonoHandle.Invoke(OnConnectionSucceeded);
                Debug.Log("服务器连接成功！");
            }
            catch (Exception e)
            {
                Debug.LogError($"服务器连接失败：{e.Message}");
                isConnecting = false;
                MonoHandle.Invoke(OnConnectionFailed, e.Message);
            }
        }

        [Obsolete("直接操作PolySocket")]
        private static void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                Socket socket = (Socket)ar.AsyncState;
                int count = socket.EndReceive(ar);
                if (count == 0)
                {
                    Close();
                    return;
                }
                readBuff.writeIndex += count;
                Debug.Log("消息开始处理。");

                ReceiveData();
                Debug.Log("消息处理完成。");

                if (readBuff.Remain < 64)
                {
                    readBuff.MoveBytes();
                    readBuff.ReSize(readBuff.Length * 2);
                }

                socket.BeginReceive(
                    readBuff.bytes, readBuff.readIndex, readBuff.Remain, 0, ReceiveCallback, socket);
            }
            catch (Exception e)
            {
                Debug.LogError("接收数据失败：" + e.Message);
            }

            void ReceiveData()
            {
                //readBuff: 0904namehello
                int msgLength = readBuff.ReadInt16();
                if (msgLength == 0 || readBuff.Length < msgLength)
                {
                    return; //处理分包
                }
                readBuff.readIndex += 2;
                //readBuff: 04namehello

                string protoName = PolyProto.MessageEncoding.DecodeName(
                    readBuff.bytes, readBuff.readIndex, out int nameCount);
                if (string.IsNullOrEmpty(protoName))
                {
                    Debug.LogError("接收到错误的数据：消息名为空！");
                    return;
                }
                readBuff.readIndex += nameCount;
                //readBuff: hello

                int bodyCount = msgLength - nameCount;
                byte[] bodyBytes = new byte[bodyCount];
                Array.Copy(readBuff.bytes, readBuff.readIndex, bodyBytes, 0, bodyCount);

                IMessage message = PolyProto.MessageEncoding.Decode(protoName, bodyBytes);
                Debug.LogInfo($"已接收数据：{msgLength} bytes - {message.GetType().Name}");

                readBuff.readIndex += bodyCount;
                readBuff.CheckAndMoveBytes();

                lock (messagesQueue)
                {
                    messagesQueue.Enqueue(message);
                    messagesQueueCount++;
                }
                if (readBuff.Length > 2)
                {
                    ReceiveData();
                }
            }
        }

        /// <summary>
        /// 初始化成员变量状态
        /// </summary>
        [Obsolete("直接new PolySocket()")]
        private static void InstantiateState()
        {
            //初始化TCP套接字
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //接收缓冲区
            readBuff = new ByteArray();
            //写入队列
            writeQueue = new Queue<ByteArray>();

            messagesQueue = new Queue<IMessage>();
            messagesQueueCount = 0;

            lastPingTime = Time.time;
            lastPongTime = Time.time;

            if (!messagesListenersPairs.ContainsKey(typeof(Msg_Net_Pong)))
            {
                AddMessageListener<Msg_Net_Pong>(OnMsgPong);
            }
        }

        [Obsolete("使用PolySocketPingExtensions的相关方法")]
        private static void OnMsgPong(IMessage message)
        {
            lastPongTime = Time.time;
            Debug.LogInfo($"ping:{GetPing()}ms");

            string GetPing()
            {
                float ping = lastPongTime - lastPingTime;
                if (ping < 0.01)
                    return "<0.01";
                if (ping < 1)
                    return string.Format("{0:N2}", ping);
                else
                    return Convert.ToInt32(ping).ToString();
            }
        }

        /// <summary>
        /// 获得当前服务器的延迟
        /// </summary>
        [Obsolete("使用PolySocketPingExtensions类中的相关扩展方法")]
        private static float GetPing()
        {
            if (!m_MainSocket.IsConnected)
            {
                return -1;
            }

            float ret = lastPongTime - lastPingTime;

            if (ret < 0.01f)
                return 0.01f;
            if (ret < 1)
                return int.Parse(string.Format("{0:N2}", ret)); //保留两位

            return Convert.ToInt32(ret); //转为整数
        }
    }
}
